package com.xxl.job.executor.service.jobhandler;

import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.executor.model.Constants;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;


/**
 * Linux命令行任务处理器
 * 用于执行系统命令并处理执行结果，包含增强的危险命令过滤机制
 * 仅允许在Linux系统中执行，其他系统将直接报错
 */
@Component
public class LinuxBashJob {


    /**
     * 命令行任务执行器
     * 处理系统命令的执行、输出日志记录和结果反馈
     * 仅允许在Linux系统中执行，其他系统直接报错
     * 参数示例："ls -a" 或者 "pwd"
     */
    @XxlJob("bash")
    public void linuxBashJobHandler() {
        // 首先检查操作系统是否为Linux
        if (!isLinuxSystem()) {
            String errorMsg = "当前操作系统不支持命令执行，仅允许在Linux系统中运行";
            XxlJobHelper.log("错误: " + errorMsg);
            XxlJobHelper.handleFail(errorMsg);
            return;
        }

        // 获取命令参数
        String command = XxlJobHelper.getJobParam();
        int exitValue = -1;

        // 使用Linux系统默认编码UTF-8
        Charset charset = StandardCharsets.UTF_8;

        BufferedReader reader = null;
        try {
            // 验证命令有效性
            if (isInvalidCommand(command)) {
                XxlJobHelper.handleFail("命令不能为空");
                return;
            }

            // 检查是否为危险命令
            if (containsDangerousCommand(command)) {
                XxlJobHelper.log("检测到危险命令，已拒绝执行: " + command);
                XxlJobHelper.handleFail("命令包含危险操作，已拦截");
                return;
            }

            // 执行命令
            Process process = executeCommand(command);

            // 读取命令输出并记录日志
            InputStream inputStream = process.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, charset));
            logCommandOutput(reader);

            // 等待命令执行完成并获取退出码
            process.waitFor();
            exitValue = process.exitValue();

        } catch (Exception e) {
            XxlJobHelper.log("命令执行异常: " + e.getMessage());
            XxlJobHelper.log(e);
        } finally {
            // 确保资源关闭
            closeReader(reader);
        }

        // 根据退出码处理任务结果
        handleCommandResult(exitValue);
    }

    /**
     * 检查当前系统是否为Linux
     *
     * @return 如果是Linux系统返回true，否则返回false
     */
    private boolean isLinuxSystem() {
        String osName = System.getProperty("os.name").toLowerCase();
        return osName.contains("linux");
    }

    /**
     * 检查命令是否包含危险操作
     *
     * @param command 待检查的命令
     * @return 是否包含危险命令
     */
    private boolean containsDangerousCommand(String command) {
        if (command == null) {
            return false;
        }

        String lowerCommand = command.toLowerCase().trim();

        // 检查是否包含危险命令
        for (String dangerous : Constants.DangerousCommands.linux) {
            // 对特殊字符进行转义处理，避免正则表达式错误
//            String escapedDangerous = dangerous.replaceAll("([/*.\\-+=()\\[\\]{}|?^$])", "\\\\$1");
            // 使用正则表达式匹配整个单词或路径
            if (lowerCommand.matches(".*\\b" + dangerous + "\\b.*") ||
                    lowerCommand.contains(dangerous) ||
                    // 检查管道和重定向操作中的危险命令
                    lowerCommand.matches(".*\\|" + dangerous + ".*") ||
                    lowerCommand.matches(".*" + dangerous + "\\|.*") ||
                    lowerCommand.matches(".*>" + dangerous + ".*") ||
                    lowerCommand.matches(".*" + dangerous + ">.*")) {
                return true;
            }
        }

        // 检查是否有重定向到敏感系统目录的操作
        String[] sensitiveDirs = {"/etc/", "/bin/", "/sbin/", "/usr/bin/", "/usr/sbin/", "/dev/", "/proc/", "/sys/"};
        for (String dir : sensitiveDirs) {
            if (lowerCommand.matches(".*>\\s*" + dir + ".*")) {
                return true;
            }
        }

        return false;
    }

    /**
     * 验证命令是否有效
     *
     * @param command 待验证的命令
     * @return 命令是否无效
     */
    private boolean isInvalidCommand(String command) {
        return command == null || command.trim().isEmpty();
    }

    /**
     * 执行系统命令
     *
     * @param command 要执行的命令
     * @return 进程对象
     * @throws Exception 执行命令时可能抛出的异常
     */
    private Process executeCommand(String command) throws Exception {
        // 使用shell执行命令，更符合Linux环境
        String[] commandArray = {"/bin/sh", "-c", command};
        ProcessBuilder processBuilder = new ProcessBuilder(commandArray);
        processBuilder.redirectErrorStream(true); // 将错误流合并到输出流
        return processBuilder.start();
    }

    /**
     * 记录命令输出日志
     *
     * @param reader 用于读取命令输出的缓冲读取器
     * @throws Exception 读取过程中可能抛出的异常
     */
    private void logCommandOutput(BufferedReader reader) throws Exception {
        String line;
        while ((line = reader.readLine()) != null) {
            XxlJobHelper.log(line);
        }
    }

    /**
     * 关闭缓冲读取器
     *
     * @param reader 要关闭的缓冲读取器
     */
    private void closeReader(BufferedReader reader) {
        if (reader != null) {
            try {
                reader.close();
            } catch (Exception e) {
                XxlJobHelper.log("关闭读取器失败: " + e.getMessage());
            }
        }
    }

    /**
     * 根据命令退出码处理任务结果
     *
     * @param exitValue 命令退出码
     */
    private void handleCommandResult(int exitValue) {
        if (exitValue == 0) {
            XxlJobHelper.handleSuccess();
        } else {
            XxlJobHelper.handleFail("命令执行失败，退出码: " + exitValue);
        }
    }
}
